﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NeuralNetworks.Networks;
using NeuralNetworks.Layers;
using NeuralNetworks.ActivationFunctions;
using NeuralNetworks.Neurons;

namespace NeuralNetworks.Learning
{
    public class DeltaRuleLearning : ISupervisedLearning
    {
        // network to teach
        private ActivationNetwork network;
        // learning rate
        private double learningRate = 0.1;

        public double LearningRate
        {
            get { return learningRate; }
            set
            {
                learningRate = Math.Max(0.0, Math.Min(1.0, value));
            }
        }

        public DeltaRuleLearning(ActivationNetwork network)
        {
            this.network = network;
        }

        public double Run(double[] input, double[] output)
        {
            // compute output of network
            double[] networkOutput = network.Compute(input);

            // get the only layer of the network
            ActivationLayer layer = network.outputLayer;
            // get activation function of the layer
            IActivationFunction activationFunction = layer.ActvFunction;

            // summary network absolute error

            double error = 0.0;

            // update weights of each neuron

            for (int j = 0, k = layer.Neurons.Count; j < k; j++)
            {
                // get neuron of the layer
                ActivationNeuron neuron = (ActivationNeuron) layer.Neurons[j];
                // calculate neuron's error
                double e = output[j] - networkOutput[j];
                // get activation function's derivative
                double functionDerivative = activationFunction.ComputeDifferential(networkOutput[j]);

                // update weights
                for (int i = 0, n = neuron.Inputs.Count; i < n; i++)
                {
                    double delta = learningRate * e * functionDerivative * input[i];
                    neuron.Inputs[i].Weight += delta;
                }

                // update threshold value
                //neuron.BiasWeight += learningRate * e * functionDerivative;

                // sum error
                error += (e * e);
            }

            return error / 2;
        }

       
        public double RunEpoch(double[][] input, double[][] output)
        {
            double error = 0.0;

            // run learning procedure for all samples
            for (int i = 0, n = input.Length; i < n; i++)
            {
                error += Run(input[i], output[i]);
            }


            // return summary error
            return error;
        }
    }
}
